/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Thread_h__
#define __dylab_Thread_h__

//===========================================================================================
#include <dylab/utils/exceptions/EOperationFailed.h>
#include <dylab/utils/exceptions/EAssertionFailed.h>

//===========================================================================================
#ifdef DYLAB_PLATFORM_WINDOWS // only Winodws platform is supported so far.
#   include <windows.h>
#   define DYLAB_THREAD_MUTEX               CRITICAL_SECTION
#   define DYLAB_THREAD_MUTEX_INIT(m)       InitializeCriticalSection(&m)
#   define DYLAB_THREAD_MUTEX_DONE(m)       DeleteCriticalSection(&m)
#   define DYLAB_THREAD_MUTEX_ACQUIRE(m)    EnterCriticalSection(&m)
#   define DYLAB_THREAD_MUTEX_RELEASE(m)    LeaveCriticalSection(&m)
#else
#   error "Only Windows platform is supported so far."
#endif

//===========================================================================================
namespace dylab {

    /** 
     * @remarks The implementation of the mutex acquire has to block ONLY when the mutex is acquired by ANOTHER thread!
     */    
    class DYLAB_EXPORT Thread
    {
	public:
        /// Provides mutualy exclusive access to the object accessed by more than one thread.
        class DYLAB_EXPORT Mutex
        {
		public:
            // RAII object for exclusive mutex acquisition
            class DYLAB_EXPORT ScopedAcquire
            {
            private:
                Mutex * mMutex;
            public:
                ScopedAcquire(Mutex * _mutex)
                    : mMutex(_mutex)
                        { DYLAB_ASSERT(mMutex != NULL); 
							mMutex->acquire(); }
                virtual ~ScopedAcquire()
                        { mMutex->release(); }
            };
            //-----------------------------------------------------------------------------
        private:
            // system object representing the critical section 
            DYLAB_THREAD_MUTEX mMutex;
        public:
            Mutex() 
				{ try { DYLAB_THREAD_MUTEX_INIT(mMutex); } catch(...) 
					{ throw EOperationFailed("Thread mutex initialisation failed."); }  }
            virtual ~Mutex()
				{ try { DYLAB_THREAD_MUTEX_DONE(mMutex); } catch(...) 
					{ throw EOperationFailed("Thread mutex deletion failed."); } }
            void acquire()
                    { try { DYLAB_THREAD_MUTEX_ACQUIRE(mMutex); } catch(...) 
						{ throw EOperationFailed("Thread mutex lock failed."); } }
            void release()
                    { try { DYLAB_THREAD_MUTEX_RELEASE(mMutex); } catch(...) 
						{ throw EOperationFailed("Thread mutex unlock failed."); }}
        };
        //-----------------------------------------------------------------------------
    public:
        // thread mutex - sync only threads in the same process
        class DYLAB_EXPORT CriticalSection
        {
        public:
            // RAII scoped lock for exclusive thread critical section lock
            class DYLAB_EXPORT ScopedExLock
            {
            private:
                CriticalSection * mCS;
            public:
                ScopedExLock(CriticalSection * _criticalSection)
                    : mCS(_criticalSection)
                        { DYLAB_ASSERT(mCS != NULL); 
							mCS->exLock(); }
                virtual ~ScopedExLock()
                        { mCS->exUnlock(); }
            };
            //-----------------------------------------------------------------------------
            // RAII scoped lock for shared thread critical section lock
            class DYLAB_EXPORT ScopedShLock
            {
            private:
                CriticalSection * mCS;
            public:
                ScopedShLock(CriticalSection * _criticalSection)
                    : mCS(_criticalSection)
                        { DYLAB_ASSERT(mCS != NULL); 
							mCS->shLock(); }
                virtual ~ScopedShLock()
                        { mCS->shUnlock(); }
			};                        							
            //-----------------------------------------------------------------------------
        private:
            // the mutex object
            Mutex mMutex;
			// counter for shared access
			uint32_t mCounter;
			// utility mutex for atomicity of the shared access operations
			Mutex mShAccessMutex;
        public:
            CriticalSection() 
				: mCounter(0)
					{ }
            virtual ~CriticalSection()
					{ }

			void exLock()
					{ mMutex.acquire(); }
			void exUnlock()
					{ mMutex.release(); }

			void shLock()
					{ Mutex::ScopedAcquire atomicLock(&mShAccessMutex); 
						if (mCounter++ == 0) mMutex.acquire(); }
			void shUnlock()
					{ Mutex::ScopedAcquire atomicUnlock(&mShAccessMutex); 
						DYLAB_ASSERT(mCounter > 0); if (--mCounter == 0) mMutex.release(); }
        };
        //-----------------------------------------------------------------------------

    public:
        ///TODO: returns the thread object that represents the current running (calling) thread        
        //*static Thread & getCurrent()

    public:

        Thread()
                { }
        virtual ~Thread()
                { }
    };
}
//===========================================================================================
#endif // __dylab_Thread_h__
